/**
* ===============================================================================================================
* Native driver for lan911x Ethernet Controller.
*
* There has nothing depedency on Operating System or upper layer protocol stack.
* Yeah, it just a pure and clean driver. You can build the TCP/IP stack based on this driver.
* ===============================================================================================================
* Reference
*
* https://ww1.microchip.com/downloads/en/DeviceDoc/00002266B.pdf
* https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/en562758.pdf
*
* ===============================================================================================================
* Copyright (c) 2024, DY Young.
* All rights reserved.
*
* Licencse Term
*----------------
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
*    this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
*    following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* ===============================================================================================================
*/

#include "lan911x_reg_p0.cc"
#include "lan911x_reg_p1.cc"
#include "lan911x_reg_p2.cc"


/** Direct memory mapped register read and write method **/
static inline void write_mmaped_reg(void* base, const int offset, const uint32 value) {
    *(volatile uint32 *)(base + offset) = value;
}

static inline uint32 read_mmaped_reg(void* base, const int offset) {
    return *(volatile uint32 *)(base + offset);
}

/* ===============================================================================================================
 *                              Here we start the functions to operate mac csr
 * ==============================================================================================================*/

void write_mac_reg(void *base, const int mac_reg_index, const uint32 value)
{
    LOCAL_U32_FEILD(vMAC_CSR_CMD)
    do
    {
        vMAC_CSR_CMD.val = read_mmaped_reg(base, MAC_CSR_CMD);
    } while (vMAC_CSR_CMD.feild.busy);

    vMAC_CSR_CMD.feild.busy = 1;
    vMAC_CSR_CMD.feild.r_nw = WRITE_MAC_REG;
    vMAC_CSR_CMD.feild.reg_index = mac_reg_index;

    write_mmaped_reg(base, MAC_CSR_DATA, value);
    write_mmaped_reg(base, MAC_CSR_CMD, vMAC_CSR_CMD.val);

    do
    {
        vMAC_CSR_CMD.val = read_mmaped_reg(base, MAC_CSR_CMD);
    } while (vMAC_CSR_CMD.feild.busy);
}

uint32 read_mac_reg(void *base, const int mac_reg_index)
{
    uint32 val = 0;
    LOCAL_U32_FEILD(vMAC_CSR_CMD)
    do
    {
        vMAC_CSR_CMD.val = read_mmaped_reg(base, MAC_CSR_CMD);
    } while (vMAC_CSR_CMD.feild.busy);

    vMAC_CSR_CMD.feild.busy = 1;
    vMAC_CSR_CMD.feild.r_nw = READ_MAC_REG;
    vMAC_CSR_CMD.feild.reg_index = mac_reg_index;

    write_mmaped_reg(base, MAC_CSR_CMD, vMAC_CSR_CMD.val);
    do
    {
        vMAC_CSR_CMD.val = read_mmaped_reg(base, MAC_CSR_CMD);
        val = read_mmaped_reg(base, MAC_CSR_DATA);
    } while (vMAC_CSR_CMD.feild.busy);

    return val;
}

void set_mac_address(void* base, const unsigned char* addr) {
    const uint32 high = addr[4] << 8 + addr[5];
    const uint32 low = addr[0] << 24 + addr[1] << 16 + addr[2] << 8 + addr[3];
    write_mac_reg(base, ADDRH, high);
    write_mac_reg(base, ADDRL, low);
}

/* ===============================================================================================================
 *                              Here we start the functions to operate MII PHY
 * ==============================================================================================================*/
/** Internal PHY access method **/


static void write_phy_reg(void *base, const int phy_addr, const int phy_reg_index, const uint32 value)
{
    LOCAL_U32_FEILD(vMII_ACC);

    do
    {
        vMII_ACC.val = read_mac_reg(base, MII_ACC);
    } while (vMII_ACC.feild.busy);

    vMII_ACC.feild.address = phy_addr;
    vMII_ACC.feild.reg_index = phy_reg_index;
    vMII_ACC.feild.w_nr = WRITE_PHY_REG;
    vMII_ACC.feild.busy = 1;

    write_mac_reg(base, MII_DATA, value);
    write_mac_reg(base, MII_ACC, vMII_ACC.val);

    do
    {
        vMII_ACC.val = read_mac_reg(base, MII_ACC);
    } while (vMII_ACC.feild.busy);
}

static uint32 read_phy_reg(void *base, const int phy_addr, const int phy_reg_index)
{
    uint32 val = 0;
    LOCAL_U32_FEILD(vMII_ACC);

    do
    {
        vMII_ACC.val = read_mac_reg(base, MII_ACC);
    } while (vMII_ACC.feild.busy);

    vMII_ACC.feild.address = phy_addr;
    vMII_ACC.feild.reg_index = phy_reg_index;
    vMII_ACC.feild.w_nr = READ_PHY_REG;
    vMII_ACC.feild.busy = 1;

    write_mac_reg(base, MII_ACC, vMII_ACC.val);

    do
    {
        val = read_mac_reg(base, MII_DATA);
        vMII_ACC.val = read_mac_reg(base, MII_ACC);
    } while (vMII_ACC.feild.busy);

    return val;
}

